---
title: Signing and Sending Transactions
description: Each transaction on Sui represents a call to a specific functionality that executes with inputs that define the result of the transaction. 
keywords: [ transactions, transaction inputs, smart contract functions, signatures, Ed25519 pure, ECDSA Secp256k1, ECDSA Secp256r1, Multisig, zkLogin ]
---

Transactions in Sui represent calls to specific functionality (like calling a smart contract function) that execute on inputs to define the result of the transaction.

Inputs can either be an object reference (either to an owned object, an immutable object, or a shared object) or an encoded value (for example, a vector of bytes used as an argument to a Move call). After a transaction is constructed, usually through using [programmable transaction blocks](/guides/developer/sui-101/building-ptb.mdx) (PTBs), the user signs the transaction and submits it to be executed on chain.

The signature is provided with the private key owned by the wallet, and its public key must be consistent with the transaction sender's Sui address.

Sui uses a `SuiKeyPair` to produce the signature, which commits to the Blake2b hash digest of the intent message (`intent || bcs bytes of tx_data`). The signature schemes currently supported are `Ed25519 Pure`, `ECDSA Secp256k1`, `ECDSA Secp256r1`, `Multisig`, and `zkLogin`.

You can instantiate `Ed25519 Pure`, `ECDSA Secp256k1`, and `ECDSA Secp256r1` using `SuiKeyPair` and use it to sign transactions. Note that this guide does not apply to `Multisig` and `zkLogin`, refer to their own pages ([Multisig](/concepts/transactions/transaction-auth/multisig) and [zkLogin](https://docs.sui.io/concepts/cryptography/zklogin) respectively) for instructions.

With a signature and the transaction bytes, a transaction can be submitted to be executed.

## Workflow

The following high-level process describes the overall workflow for constructing, signing, and executing an on-chain transaction:

- Construct the transaction data by creating a `Transaction` where multiple transactions are chained. See [Building Programmable Transaction Blocks](/guides/developer/sui-101/building-ptb.mdx) for more information.
- The SDK's built-in gas estimation and coin selection picks the gas coin.
- Sign the transaction to generate a [signature](/concepts/transactions/transaction-auth.mdx).
- Submit the `Transaction` and its signature for on-chain execution.

:::info

If you want to use a specific gas coin, first find the gas coin object ID to be used to pay for gas and explicitly use that in the PTB. If there is no gas coin object, use the [splitCoin](./building-ptb#available-transactions) transaction to create a gas coin object. The split coin transaction should be the first transaction call in the PTB.

:::

## Process multiple transactions from the same address

The Sui SDK offers transaction executors to help process multiple transactions from the same address.

### `SerialTransactionExecutor`

Use the `SerialTransactionExecutor` when you process transactions one after another. The executor grabs all the coins from the sender and combines them into a single coin that is used for all transactions.

Using the `SerialTransactionExecutor` prevents `SequenceNumber` errors by handling the versioning of object inputs across a PTB.

### `ParallelTransactionExecutor`

Use the `ParallelTransactionExecutor` when you want to process transactions with the same sender at the same time. This class creates a pool of gas coins that it manages to ensure parallel transactions don't equivocate those coins. The class tracks objects used across transactions and orders their processing so that the object inputs are also not equivocated.

## Examples

The following examples demonstrate how to sign and execute transactions using Rust, TypeScript, or the Sui CLI.

<Tabs groupId="code-language">

<TabItem value="ts-sdk" label="TypeScript">

There are various ways to instantiate a key pair and to derive its public key and Sui address using the Sui TypeScript SDK.

```tsx
import { fromHex } from '@mysten/bcs';
import { getFullnodeUrl, SuiClient } from '@mysten/sui/client';
import { type Keypair } from '@mysten/sui/cryptography';
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
import { Secp256k1Keypair } from '@mysten/sui/keypairs/secp256k1';
import { Secp256r1Keypair } from '@mysten/sui/keypairs/secp256r1';
import { Transaction } from '@mysten/sui/transactions';

const kp_rand_0 = new Ed25519Keypair();
const kp_rand_1 = new Secp256k1Keypair();
const kp_rand_2 = new Secp256r1Keypair();

const kp_import_0 = Ed25519Keypair.fromSecretKey(
	fromHex('0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82'),
);
const kp_import_1 = Secp256k1Keypair.fromSecretKey(
	fromHex('0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82'),
);
const kp_import_2 = Secp256r1Keypair.fromSecretKey(
	fromHex('0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82'),
);

// $MNEMONICS refers to 12/15/18/21/24 words from the wordlist, for example, "retire skin goose will hurry this field stadium drastic label husband venture cruel toe wire". Refer to [Keys and Addresses](/concepts/transactions/transaction-auth.mdx) for more.
const kp_derive_0 = Ed25519Keypair.deriveKeypair('$MNEMONICS');
const kp_derive_1 = Secp256k1Keypair.deriveKeypair('$MNEMONICS');
const kp_derive_2 = Secp256r1Keypair.deriveKeypair('$MNEMONICS');

const kp_derive_with_path_0 = Ed25519Keypair.deriveKeypair('$MNEMONICS', "m/44'/784'/1'/0'/0'");
const kp_derive_with_path_1 = Secp256k1Keypair.deriveKeypair('$MNEMONICS', "m/54'/784'/1'/0/0");
const kp_derive_with_path_2 = Secp256r1Keypair.deriveKeypair('$MNEMONICS', "m/74'/784'/1'/0/0");

// replace `kp_rand_0` with the variable names above.
const pk = kp_rand_0.getPublicKey();
const sender = pk.toSuiAddress();

// create an example transaction block.
const txb = new Transaction();
txb.setSender(sender);
txb.setGasPrice(5);
txb.setGasBudget(100);
const bytes = await txb.build();
const serializedSignature = (await keypair.signTransaction(bytes)).signature;

// verify the signature locally
expect(await keypair.getPublicKey().verifyTransaction(bytes, serializedSignature)).toEqual(true);

// define sui client for the desired network.
const client = new SuiClient({ url: getFullnodeUrl('testnet') });

// execute transaction.
let res = client.executeTransactionBlock({
	transactionBlock: bytes,
	signature: serializedSignature,
});
console.log(res);
```

</TabItem>

<TabItem value="rust" label="Rust">

The full code example below can be found under [crates/sui-sdk](https://github.com/MystenLabs/sui/blob/main/crates/sui-sdk/examples/sign_tx_guide.rs).

There are various ways to instantiate a `SuiKeyPair` and to derive its public key and Sui address using the Sui Rust SDK.

```rust
    // deterministically generate a key pair, testing only, do not use for mainnet, use the next section to randomly generate a key pair instead.
    let skp_determ_0 =
        SuiKeyPair::Ed25519(Ed25519KeyPair::generate(&mut StdRng::from_seed([0; 32])));
    let _skp_determ_1 =
        SuiKeyPair::Secp256k1(Secp256k1KeyPair::generate(&mut StdRng::from_seed([0; 32])));
    let _skp_determ_2 =
        SuiKeyPair::Secp256r1(Secp256r1KeyPair::generate(&mut StdRng::from_seed([0; 32])));

    // randomly generate a key pair.
    let _skp_rand_0 = SuiKeyPair::Ed25519(get_key_pair_from_rng(&mut rand::rngs::OsRng).1);
    let _skp_rand_1 = SuiKeyPair::Secp256k1(get_key_pair_from_rng(&mut rand::rngs::OsRng).1);
    let _skp_rand_2 = SuiKeyPair::Secp256r1(get_key_pair_from_rng(&mut rand::rngs::OsRng).1);

    // import a key pair from a base64 encoded 32-byte `private key`.
    let _skp_import_no_flag_0 = SuiKeyPair::Ed25519(Ed25519KeyPair::from_bytes(
        &Base64::decode("1GPhHHkVlF6GrCty2IuBkM+tj/e0jn64ksJ1pc8KPoI=")
            .map_err(|_| anyhow!("Invalid base64"))?,
    )?);
    let _skp_import_no_flag_1 = SuiKeyPair::Ed25519(Ed25519KeyPair::from_bytes(
        &Base64::decode("1GPhHHkVlF6GrCty2IuBkM+tj/e0jn64ksJ1pc8KPoI=")
            .map_err(|_| anyhow!("Invalid base64"))?,
    )?);
    let _skp_import_no_flag_2 = SuiKeyPair::Ed25519(Ed25519KeyPair::from_bytes(
        &Base64::decode("1GPhHHkVlF6GrCty2IuBkM+tj/e0jn64ksJ1pc8KPoI=")
            .map_err(|_| anyhow!("Invalid base64"))?,
    )?);

    // import a key pair from a base64 encoded 33-byte `flag || private key`. The signature scheme is determined by the flag.
    let _skp_import_with_flag_0 =
        SuiKeyPair::decode_base64("ANRj4Rx5FZRehqwrctiLgZDPrY/3tI5+uJLCdaXPCj6C")
            .map_err(|_| anyhow!("Invalid base64"))?;
    let _skp_import_with_flag_1 =
        SuiKeyPair::decode_base64("AdRj4Rx5FZRehqwrctiLgZDPrY/3tI5+uJLCdaXPCj6C")
            .map_err(|_| anyhow!("Invalid base64"))?;
    let _skp_import_with_flag_2 =
        SuiKeyPair::decode_base64("AtRj4Rx5FZRehqwrctiLgZDPrY/3tI5+uJLCdaXPCj6C")
            .map_err(|_| anyhow!("Invalid base64"))?;

    // replace `skp_determ_0` with the variable names above
    let pk = skp_determ_0.public();
    let sender = SuiAddress::from(&pk);
```

Next, sign transaction data constructed using an example programmable transaction block with default gas coin, gas budget, and gas price. See [Building Programmable Transaction Blocks](./building-ptb.mdx) for more information.

```rust
    // construct an example programmable transaction.
    let pt = {
        let mut builder = ProgrammableTransactionBuilder::new();
        builder.pay_sui(vec![sender], vec![1])?;
        builder.finish()
    };

    let gas_budget = 5_000_000;
    let gas_price = sui_client.read_api().get_reference_gas_price().await?;

    // create the transaction data that will be sent to the network.
    let tx_data = TransactionData::new_programmable(
        sender,
        vec![gas_coin.object_ref()],
        pt,
        gas_budget,
        gas_price,
    );
```

Commit a signature to the Blake2b hash digest of the intent message (`intent || bcs bytes of tx_data`).

```rust
    // derive the digest that the key pair should sign on, that is, the blake2b hash of `intent || tx_data`.
    let intent_msg = IntentMessage::new(Intent::sui_transaction(), tx_data);
    let raw_tx = bcs::to_bytes(&intent_msg).expect("bcs should not fail");
    let mut hasher = sui_types::crypto::DefaultHash::default();
    hasher.update(raw_tx.clone());
    let digest = hasher.finalize().digest;

    // use SuiKeyPair to sign the digest.
    let sui_sig = skp_determ_0.sign(&digest);

    // if you would like to verify the signature locally before submission, use this function. if it fails to verify locally, the transaction will fail to execute in Sui.
    let res = sui_sig.verify_secure(
        &intent_msg,
        sender,
        sui_types::crypto::SignatureScheme::ED25519,
    );
    assert!(res.is_ok());
```

Finally, submit the transaction with the signature.

```rust
    let transaction_response = sui_client
        .quorum_driver_api()
        .execute_transaction_block(
            sui_types::transaction::Transaction::from_generic_sig_data(
                intent_msg.value,
                Intent::sui_transaction(),
                vec![GenericSignature::Signature(sui_sig)],
            ),
            SuiTransactionBlockResponseOptions::default(),
            None,
        )
        .await?;
```

</TabItem>

<TabItem value="cli" label="Sui CLI">

When using the [Sui CLI](/references/cli.mdx) for the first time, it creates a local file in `~/.sui/keystore` on your machine with a list of private keys (encoded as Base64 encoded `flag || 32-byte-private-key`). You can use any key to sign transactions by specifying its address. Use `sui keytool list` to see a list of addresses.

There are 3 ways to initialize a key:

```sh
# generate randomly.
sui client new-address ed25519
sui client new-address secp256k1
sui client new-address secp256r1

# import the 32-byte private key to keystore.
sui keytool import "0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82" ed25519
sui keytool import "0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82" secp256k1
sui keytool import "0xd463e11c7915945e86ac2b72d88b8190cfad8ff7b48e7eb892c275a5cf0a3e82" secp256r1

# import the mnemonics (recovery phrase) with derivation path to keystore.
# $MNEMONICS refers to 12/15/18/21/24 words from the wordlist, for example, "retire skin goose will hurry this field stadium drastic label husband venture cruel toe wire". Refer to [Keys and Addresses](/concepts/transactions/transaction-auth.mdx) for more.

sui keytool import "$MNEMONICS" ed25519
sui keytool import "$MNEMONICS" secp256k1
sui keytool import "$MNEMONICS" secp256r1
```

Create a transfer transaction in the CLI. Set the `$SUI_ADDRESS` to the one corresponding to the key pair used to sign. `$GAS_COIN_ID` refers to the object ID that is owned by the sender to be used as gas. `$GAS_BUDGET` refers to the budget used to execute transaction. Then sign with the private key corresponding to the sender address. `$MNEMONICS` refers to 12/15/18/21/24 words from the wordlist, for example, "retire skin goose will hurry this field stadium drastic label husband venture cruel toe wire". Refer to [Keys and Addresses](/concepts/transactions/transaction-auth.mdx) for more.

<ImportContent source="info-gas-budget.mdx" mode="snippet" />

```sh
$ sui client gas
```
```sh
$ sui client transfer-sui --to $SUI_ADDRESS --sui-coin-object-id $GAS_COIN_ID --gas-budget $GAS_BUDGET --serialize-unsigned-transaction
```
```sh
$ sui keytool sign --address $SUI_ADDRESS --data $TX_BYTES
```
```sh
$ sui client execute-signed-tx --tx-bytes $TX_BYTES --signatures $SERIALIZED_SIGNATURE
```

</TabItem>
</Tabs>

### Notes

1. This guide demonstrates how to sign with a single private key. Refer to [Multisig](/concepts/transactions/transaction-auth/multisig.mdx) when it is preferred to set up more complex signing policies.
2. Similarly, native zkLogin does not follow the above steps, see [the docs](/concepts/cryptography/zklogin.mdx) to understand how to derive a zkLogin address and produce a zkLogin signature with an ephemeral key pair.
3. If you decide to implement your own signing mechanisms instead of using the previous tools, see the [Signatures](/concepts/transactions/transaction-auth.mdx) doc on the accepted signature specifications for each scheme.
4. Flag is 1 byte that differentiates signature schemes. See supported schemes and its flag in [Signatures](/concepts/transactions/transaction-auth.mdx).
5. The `execute_transaction_block` endpoint takes a list of signatures, so it should contain exactly 1 user signature, unless you are using sponsored transaction that a second signature for the gas object can be provided. See [Sponsored Transactions](/concepts/transactions/sponsored-transactions.mdx) for more information.